/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年5月23日
 */
package com.massyframework.beanskin.graphql.context;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;

import javax.annotation.Resource;

import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyContextSupport;
import com.massyframework.beanskin.util.IOUtils;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;

import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;

/**
 * {@link SimleGraphQLModular}, 简单的GraphQL模块
 * @author huangkh
 *
 */
public class DefaultGraphQLModular extends AssemblyContextSupport 
	implements GraphQLModular {
	
	private String schemaFile = "BEANSKIN-INF/config/domain.graphql";
	private RuntimeWiringFactory runtimeWiringFactory;
	private Set<GraphQLModular> requireModulars;
	
	private TypeDefinitionRegistry registry;
	private RuntimeWiring runtimeWiring;

	/**
	 * 无参的构造方法
	 */
	public DefaultGraphQLModular() {
		
	}

	@Override
	public TypeDefinitionRegistry getTypeDefinitionRegistry(SchemaParser parser) {
		if (this.registry == null) {
			Objects.requireNonNull(this.schemaFile, "\"schemaFile\" cannot be null, please set it.");
			
			URL url = null;
			Assembly assembly = this.getAssemblyContext().getAssociatedAssembly();
			ModuleClassLoader classLoader =
					assembly.getModuleClassLoader();
			url = classLoader.getResource(this.schemaFile);

			InputStream is = null;
			URLConnection conn = null;
			try {
				conn = url.openConnection();
				conn.setUseCaches(false);
				is = url.openStream();
				this.registry =  parser.parse(is);
			}catch(Exception e) {
				throw new RuntimeException("load graphql schema failed: file=" + this.schemaFile + ".", e);
			}finally {
				IOUtils.close(is);
			}
		}
		
		return this.registry;
		
	}
	

	@Override
	public RuntimeWiring getRuntimeWiring() {
		if (this.runtimeWiring == null) {
			this.runtimeWiring =  this.getRuntimeWiringFactory()
					.createRuntimeWiring(this.getAssemblyContext().getAssociatedAssembly());
		}
		return this.runtimeWiring;
	}

	/**
	 * 运行植入工厂
	 * @return {@link RuntimeWiringFactory}
	 */
	public RuntimeWiringFactory getRuntimeWiringFactory() {
		return runtimeWiringFactory;
	}

	/**
	 * 设置运行植入工厂
	 * @param runtimeWiringFactory {@link RuntimeWiringFactory}
	 */
	@Resource
	public void setRuntimeWiringFactory(RuntimeWiringFactory runtimeWiringFactory) {
		this.runtimeWiringFactory = runtimeWiringFactory;
	}

	/**
	 * GraphQL的模型文件，存放在ClassPath中
	 * @return {@link String}
	 */
	public String getSchemaFile() {
		return schemaFile;
	}

	/**
	 * 设置GraphQL的模型文件，存放在ClassPath中。
	 * @param schemaFile {@link String}
	 */
	public void setSchemaFile(String schemaFile) {
		this.schemaFile = schemaFile;
	}

	/**
	 * 依赖的其他GraphQL模块
	 * @return {@link Set}
	 */
	public Set<GraphQLModular> getRequireModulars() {
		if (this.requireModulars == null) {
			return Collections.emptySet();
		}
		return requireModulars;
	}

	/**
	 * 设置依赖的模块
	 * @param requireModulars
	 */
	public void setRequireModulars(Set<GraphQLModular> requireModulars) {
		this.requireModulars = Collections.unmodifiableSet(requireModulars);
	}

	@Override
	public int compareTo(GraphQLModular o) {
		if (o == null) return 0;
		
		if (this.getRequireModulars().contains(o)) {
			return 1;
		}
		if (o.getRequireModulars().contains(this)) {
			return -1;
		}
		
		return 0;
	}
	
	
}
